using System;
using System.Collections.Generic;
using System.Text;

using CILUtil;

using Mono.Cecil;

namespace CILDiffLibPrime
{
    class MatchResult<T>
    {
        public List<T> NewElements = new List<T>();
        public List<T> DeletedElements = new List<T>();
        public List<EntityVersion<T>> CommonElements = new List<EntityVersion<T>>();
    }

    class EntityVersion<E>
    {
        public EntityVersion(E old, E n)
        {
            this.Original = old;
            this.Target = n;
        }
        public E Original;
        public E Target;
    }

    delegate string Name(object t);
    public enum StructureMatch
    {
        Type,
        Method,
        Field,
        Property,
        Event,
        Pair,
        PairEntity
    }

    class Collector
    {
        static Name className = delegate(object tr) { return CodeProperties.GetClassName((TypeReference)tr); };

        static Name methodName = delegate(object md)
            { return CodeProperties.GetMethodNameWithParametersTight((MethodDefinition)md, true); };

        static Name memberReference = delegate(object field) 
            { return ((IMemberReference)field).Name; };

        static Name pairReference = delegate(object pair)
            { return ((CILFlow.CILPair)pair).Name; };

        static Name pairEntity = delegate(object pairEntity)
            { return ((CILFlow.PairEntity)pairEntity).Name; };
        
        static public MatchResult<T> CollectMatches<T>(object a, object b, StructureMatch matchType)
        {
            Dictionary<String, T> htA = Collect<T>(a, matchType);
            Dictionary<String, T> htB = Collect<T>(b, matchType);

            Dictionary<String, T> deletedTypes = new Dictionary<String, T>();
            Dictionary<String, EntityVersion<T>> commonTypes = new Dictionary<String, EntityVersion<T>>();
            Dictionary<String, T> newTypes = new Dictionary<String, T>();

            MatchResult<T> result = Match.CollectCandidateMatches(htA, htB, deletedTypes, commonTypes, newTypes);
            return result;
        }

        static private Dictionary<String, T> Collect<T>(object a, StructureMatch matchType)
        {
            if (matchType == StructureMatch.Type)
            {
                return CollectTypes<T>((AssemblyDefinition) a);
            }
            if (matchType == StructureMatch.Method)
            {
                System.Collections.ArrayList list = new System.Collections.ArrayList();
                TypeDefinition type = (TypeDefinition)a;
                list.AddRange(type.Methods);
                list.AddRange(type.Constructors);
                return CollectElement<T>(list, methodName );
            }
            if (matchType == StructureMatch.Event)
            {
                return CollectElement<T>(((TypeDefinition)a).Events, memberReference);
            }
            if (matchType == StructureMatch.Field)
            {
                return CollectElement<T>(((TypeDefinition)a).Fields, memberReference);
            }
            if (matchType == StructureMatch.Property)
            {
                return CollectElement<T>(((TypeDefinition)a).Properties, memberReference);
            }
            if (matchType == StructureMatch.Pair)
            {
                return CollectElement<T>(((List<CILFlow.CILPair>)a), pairReference);
            }
            if (matchType == StructureMatch.PairEntity)
            {
                return CollectElement<T>(((List<CILFlow.PairEntity>)a), pairEntity);
            }
            return null;
        }

        private static Dictionary<String, T> CollectElement<T>( System.Collections.ICollection collection, 
            Name nameFunc )
        {
            Dictionary<String, T> ht = new Dictionary<String, T>();
            foreach (T t in collection)
            {
                ht[nameFunc(t)] = t;
            }
            return ht;
        }

        static public Dictionary<String, T> CollectTypes<T>(AssemblyDefinition assem)
        {
            Dictionary<String, T> ht = new Dictionary<String, T>();
            foreach (ModuleDefinition md in assem.Modules)
            {
                foreach (T td in md.Types)
                {
                    CollectTypes(td, ht);
                }
            }
            return ht;
        }

        private static System.Collections.ICollection GetInner(object t)
        {
            return ((TypeDefinition)t).NestedTypes;
        }

        static public void CollectTypes<T>(T td, Dictionary<String, T> ht)
        {
            ht[className(td)] = td;
            foreach (T inner in GetInner(td))
            {
                CollectTypes(inner, ht);
            }
        }
    }


    class Match
    {
        static public MatchResult<T> CollectCandidateMatches<T>(Dictionary<String, T> htA, Dictionary<String, T> htB,
           Dictionary<String, T> deletedTypes, Dictionary<String, EntityVersion<T>> commonTypes, Dictionary<String, T> newTypes)
        {
            foreach (string name in htB.Keys)
            {
                T m = htB[name];
                if (htA.ContainsKey(name))
                {
                    commonTypes[name] = new EntityVersion<T>((T)htA[name], m);
                }
                else
                {   // This is a new entity.
                    newTypes[name] = m;
                }
            }

            foreach (string name in htA.Keys)
            {
                if (!commonTypes.ContainsKey(name))
                {   // This was deleted entity.
                    deletedTypes[name] = htA[name];
                }
            }

            MatchResult<T> match = new MatchResult<T>();
            match.CommonElements  = new List<EntityVersion<T>>(commonTypes.Values);
            match.DeletedElements = new List<T>( deletedTypes.Values );
            match.NewElements     = new List<T>( newTypes.Values );
            return match;
        }
    }
}
